﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CompilerLib.Scanner.Generator
{
    public class NFAConverter : RegularExpressionConverter<NFAModel>
    {

        public override NFAModel ConvertSymbol(SymbolExpression exp)
        {
            NFAModel m = new NFAModel();
            NFAState s = new NFAState();
            NFAEdge e = new NFAEdge(exp.InnerSymbol, s);
            m.EntryEdge = e;
            m.AddState(s);
            m.TailState = s;
            return m;
        }

        public override NFAModel ConvertEmpty(EmptyExpression exp)
        {
            NFAState tail = new NFAState();
            NFAEdge entryEdge = new NFAEdge(tail);
            NFAModel nfa = new NFAModel();
            nfa.EntryEdge = entryEdge;
            nfa.AddState(tail);
            nfa.TailState = tail;
            return nfa;
        }

        public override NFAModel ConvertConcatenation(ConcatenationExpression exp)
        {
            var leftNFA = Convert(exp.Left);
            var rightNFA = Convert(exp.Right);
            var concatenationNfa = new NFAModel();

            concatenationNfa.AddStates(leftNFA.States);
            concatenationNfa.AddStates(rightNFA.States);

            leftNFA.TailState.AddEdge(rightNFA.EntryEdge);

            concatenationNfa.EntryEdge = leftNFA.EntryEdge;
            concatenationNfa.TailState = rightNFA.TailState;

            return concatenationNfa;
        }

        public override NFAModel ConvertKleeneStar(KleeneStarExpression exp)
        {
            var nfa = Convert(exp.innerExpression);


            NFAState tail = new NFAState();
            NFAEdge entry = new NFAEdge(tail);

            nfa.TailState.AddEmptyEdgeTo(tail);
            tail.AddEdge(nfa.EntryEdge);

            NFAModel kleeneStartNFA = new NFAModel();
            kleeneStartNFA.EntryEdge = entry;
            kleeneStartNFA.AddStates(nfa.States);
            kleeneStartNFA.AddState(tail);
            kleeneStartNFA.TailState = tail;
            return kleeneStartNFA;
        }
        public override NFAModel ConvertAlternation(AlternationExpression exp)
        {
            var nfa1 = Convert(exp.Expression1);
            var nfa2 = Convert(exp.Expression2);

            NFAState head = new NFAState();
            NFAState tail = new NFAState();

            head.AddEdge(nfa1.EntryEdge);
            head.AddEdge(nfa2.EntryEdge);

            nfa1.TailState.AddEmptyEdgeTo(tail);
            nfa2.TailState.AddEmptyEdgeTo(tail);

            NFAModel alternationNFA = new NFAModel();

            alternationNFA.AddState(head);
            alternationNFA.AddStates(nfa1.States);
            alternationNFA.AddStates(nfa2.States);
            alternationNFA.AddState(tail);

            alternationNFA.EntryEdge = new NFAEdge(head);
            alternationNFA.TailState = tail;
            return alternationNFA;
        }
    }
}
